package com.yuqih.common.dto;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.Id;

import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.util.ReflectionUtils;

import com.yuqih.common.annotation.CompositeId;


public class TypeDescriptor {

	private Map<String, FieldDescriptor> fieldDescriptorMapping;
	private final Class<?> clazz;
	private FieldDescriptor idFieldDescriptor;
	private PropertyDescriptor[] propertyDescriptors;
	
	/**
	 * @param clazz
	 */
	public TypeDescriptor(Class<?> clazz) {
		super();
		this.clazz = clazz;
	}

	public static TypeDescriptor by(Class<?> clazz) {
		PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors(clazz);

		Map<String, FieldDescriptor> fieldDescriptorMapping = new HashMap<String, FieldDescriptor>();

		for (PropertyDescriptor pd : pds) {
			if (pd.getReadMethod() == null) {
				continue;
			}
			if (pd.getWriteMethod() == null) {
				continue;
			}
			Field field = ReflectionUtils.findField(clazz, pd.getName());
			if (field == null) {
				continue;
			}

			FieldDescriptor fieldDescriptor = new FieldDescriptor();
			fieldDescriptor.setField(field);
			fieldDescriptor.setFieldName(pd.getName());
			fieldDescriptor.setReadMethod(pd.getReadMethod());
			fieldDescriptor.setWriteMethod(pd.getWriteMethod());
			fieldDescriptor.setFieldType(field.getType());
			if (List.class.equals(field.getType())) {
				ParameterizedType parameterizedType = (ParameterizedType) field
						.getGenericType();
				fieldDescriptor.setItemType((Class<?>) parameterizedType.getActualTypeArguments()[0]);
			} 

			fieldDescriptorMapping.put(pd.getName(), fieldDescriptor);
		}

		TypeDescriptor modelDescriptor = new TypeDescriptor(clazz);
		modelDescriptor.propertyDescriptors = pds;
		modelDescriptor.fieldDescriptorMapping = Collections.unmodifiableMap(fieldDescriptorMapping);
		
		return modelDescriptor;
	}

	/**
	 * @return the fieldDescriptorMapping
	 */
	public Collection<FieldDescriptor> getFieldDescriptors() {
		return fieldDescriptorMapping.values();
	}
	
	/**
	 * @return the fieldDescriptorMapping
	 */
	public Set<String> getFieldNames() {
		return fieldDescriptorMapping.keySet();
	}
	
	public boolean containsField(String field) {
		return fieldDescriptorMapping.containsKey(field);
	}
	
	public FieldDescriptor getIdFieldDescriptor() {
		
		if (idFieldDescriptor != null) {
			return idFieldDescriptor;
		}
		
		List<FieldDescriptor> ids = new ArrayList<>();
		for (FieldDescriptor fieldDescriptor : fieldDescriptorMapping.values()) {
			if (fieldDescriptor.getAnnotation(Id.class) != null) {
				ids.add(fieldDescriptor);
			}
		}
		
		if (ids.size() == 1) {
			idFieldDescriptor = ids.get(0);
			return idFieldDescriptor;
		}
		
		List<PropertyDescriptor> list = new ArrayList<PropertyDescriptor>();
		for (PropertyDescriptor pd : propertyDescriptors) {
			if (pd.getReadMethod() == null) {
				continue;
			}
			
			CompositeId compositeId = pd.getReadMethod().getAnnotation(CompositeId.class);
			if (compositeId == null) {
				continue;
			}
			
			list.add(pd);
		}
		
		if (list.size() != 1) {
			throw new IllegalArgumentException("类型[" + clazz + "]未设置id标签");
		}
		
		PropertyDescriptor pd = list.get(0);
		FieldDescriptor fieldDescriptor = new FieldDescriptor();
		fieldDescriptor.setFieldName(pd.getName());
		fieldDescriptor.setReadMethod(pd.getReadMethod());
		fieldDescriptor.setWriteMethod(pd.getWriteMethod());
		fieldDescriptor.setFieldType(pd.getPropertyType());
		
		idFieldDescriptor = fieldDescriptor;
		return idFieldDescriptor;
	}

}
